﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace PdfFormHandler
{
    public class PdfForm : SPItemEventReceiver
    {
        /// <summary>
        /// ItemAdded - Runs when a PDF Form is added.
        /// </summary>
        /// <param name="properties"></param>
        public override void ItemAdded(SPItemEventProperties properties)
        {
            base.ItemAdded(properties);
            this.DisableEventFiring();
            UpdateItemProperties(properties);
            SetPermissions(properties);
            this.EnableEventFiring();

        }

        /// <summary>
        /// ItemUpdated - Runs when a PDF Form is updated
        /// </summary>
        /// <param name="properties"></param>
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            base.ItemUpdated(properties);

            // Don't do any of these updates if the item just got Approved or Rejected.
            Int16 approvalStatus = -999;
            if (properties.ListItem.Fields["Approval Status"] != null)
            {
                approvalStatus = Convert.ToInt16(properties.ListItem["Approval Status"].ToString());
            }

            // 0 = Approved; 1 = Rejected
            if (approvalStatus != 0 && approvalStatus != 1)
            {
                this.DisableEventFiring();
                UpdateItemProperties(properties);
                // SetPermissions(properties);
                this.EnableEventFiring();
            }
        }

        /// <summary>
        /// Update Item Properties
        /// Opens up the PDF that was just added/modified in the Sharepoint library.
        /// For the item, iterates through all content type properties. 
        /// If it finds a corresponding property in the Sharepoint item list, then it takes the value from the PDF and places into the SP list item.
        /// </summary>
        /// <param name="properties"></param>
        private void UpdateItemProperties(SPItemEventProperties properties)
        {
            System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
            xmlDocument.LoadXml(properties.ListItem.Xml);

            SPListItem listItem = properties.ListItem;

            SPFile spFile = properties.ListItem.File;
            Byte[] binaryFile = spFile.OpenBinary();

            PdfReader pdfReader = new PdfReader(properties.ListItem.File.OpenBinary(), null); 
            AcroFields pdfFormFields = pdfReader.AcroFields;

            Boolean itemNeedsUpdating = false;

            // Iterate through all fields marked for PDF and find corresponding property in PDF document
            foreach (SPField spField in listItem.Fields)
            {
                String pdfAttributeName = "";
                String pdfAttributeValue = "";

                if (spField.Title.Length >= 4 && spField.Title.Substring(0, 4).ToLower() == "pdf_")
                {
                    // Get attribute name
                    // Sharepoint field name come up with _x0020 instead of space when accessing programmatically
                    // Two underlines in the name will be converted to a space in the PDF column name
                    pdfAttributeName = spField.Title.Substring(4, spField.Title.Length - 4).Replace("_x0020_", " ").Replace("__", " ");
                    pdfAttributeValue = pdfFormFields.GetField(pdfAttributeName);
                    if (pdfAttributeValue != null)
                    {
                        if (listItem[spField.Title] == null || listItem[spField.Title].ToString() != pdfAttributeValue)
                        {
                            listItem[spField.Title] = pdfAttributeValue;
                            itemNeedsUpdating = true;
                        }
                    }
                }
            }

            // Update the filename to guarantee uniqueness
            String fileName;
            fileName = listItem["Content Type"].ToString() + " - " + listItem["ID"].ToString() + " - " + listItem["GUID"].ToString().Replace("{", "").Replace("}", "");
            fileName = fileName.Replace(" ", "_");
            if (listItem["Name"].ToString() != fileName)
            {
                listItem["Name"] = fileName;
                itemNeedsUpdating = true;
            }

            // Commit changes, but only if there's something to commit (otherwise, it adds an unnecessary version to the document's version history.
            if (itemNeedsUpdating)
            {
                listItem.Update();
            }

        } // UpdateItemProperties()




        /// <summary>
        /// 
        /// </summary>
        /// <param name="properties"></param>
        private void SetPermissions(SPItemEventProperties properties)
        {
            System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
            xmlDocument.LoadXml(properties.ListItem.Xml);

            string ReturnVal = "";
            try
            {
                SPWeb spWeb = properties.ListItem.ParentList.ParentWeb;
                SPList spList = properties.ListItem.ParentList;

                // Break the permissions on the List Item so they can be edited.
                if (!properties.ListItem.HasUniqueRoleAssignments)
                {
                    properties.ListItem.BreakRoleInheritance(true);
                    properties.ListItem.Update();
                }

                // Then, delete all the permissions copied down from the list except for those with Full Control.
                // Things go screwy in Sharepoint when we remove things out of the collection, so we're going to have to iterate and delete
                // That is, continue iterating through all users and roles until only users with Full Control are left.
                spWeb.AllowUnsafeUpdates = true;
                bool stillCheckPermissions = true;
                bool itemNeedsUpdating = false;

                while (stillCheckPermissions)
                {
                    stillCheckPermissions = false;
                    foreach (SPRoleAssignment roleAssignment in properties.ListItem.RoleAssignments)
                    {
                        // TODO: Also disregard if it's the employee themselves.

                        bool deletePermission = true;
                        // Most all user entries have just one role assignmenent, but it's better to check all to be sure.
                        foreach (SPRoleDefinition spRoleAssignmentDefinition in roleAssignment.RoleDefinitionBindings)
                        {
                            if (spRoleAssignmentDefinition.Name == "Full Control") { deletePermission = false; }
                            if (spRoleAssignmentDefinition.Name == "Approve") { deletePermission = false; }
                        }

                        // If there wasn't a single Full Control role assignment found, then remove the role.
                        if (deletePermission)
                        {
                            // Whenever we delete one, we get to "reshuffle" and check again until we don't find anyone that needs to be removed.
                            stillCheckPermissions = true;
                            properties.ListItem.RoleAssignments.Remove(roleAssignment.Member);
                            itemNeedsUpdating = true;
                            break;
                        }
                    }
                }
                if (itemNeedsUpdating)
                {
                    properties.ListItem.Update();
                }

                // Add the Employee as a user with Contribute rights. 
                // If they're already on the permissions list, this action will not error, but will replace the current permission with the new permission

                SPUser spUser = spWeb.SiteUsers.GetByID(Convert.ToInt32(properties.ListItem["Employee"].ToString().Substring(0, properties.ListItem["Employee"].ToString().IndexOf(";"))));
                SPRoleAssignment spRoleAssignment = new SPRoleAssignment(spUser.LoginName, spUser.Email, spUser.Name, string.Empty);
                SPRoleDefinition spRoleDefinition = properties.ListItem.Web.RoleDefinitions["Contribute"];
                spRoleAssignment.RoleDefinitionBindings.Add(spRoleDefinition);

                // Add the user and their role to the List Item.
                // If the user's id is bogus (i.e. the SID is not found in domain most likely), then this will err.
                try
                {
                    properties.ListItem.RoleAssignments.Add(spRoleAssignment);
                }
                catch (Exception ex)
                {
                    ReturnVal += "Permission not set, reason: " + ex.Message + "List Item Id: " + properties.ListItem.ID;
                }
                properties.ListItem.Update();

                spWeb.AllowUnsafeUpdates = false;
                spWeb.Dispose();

            }
            catch (Exception ex)
            {
                ReturnVal += "Permission not set, reason: " + ex.Message + "List Item Id: " + properties.ListItem.ID;
            }

        } // SetPermissions()

    }

}
